<template>
  <view class="custom-tree-select-list">
    <view v-if="items.length === 0" class="tree-select-list_empty">
      <custom-empty />
    </view>
    <view v-else class="tree-select-list-node" v-for="item in items" :key="item[fieldsName.key]">
      <view class="tree-select-list-node-holder">
        <view
          class="tree-select-list-node-switcher"
          :class="{
            'tree-select-list-node-switcher_open': expandList.includes(`${item[fieldsName.key]}`),
            'tree-select-list-node-switcher_disbaled': !valueNotEmpty(item[fieldsName.children]),
          }"
          @click="onExpandChecked(`${item[fieldsName.key]}`)"
        >
        </view>
        <view class="tree-select-list-node-content" @click="onChecked(item[fieldsName.key], item)">
          <custom-checkbox :value="value.includes(`${item[fieldsName.key]}`)">
            {{ item[fieldsName.label] }}
          </custom-checkbox>
        </view>
      </view>

      <!-- children list -->
      <view
        class="tree-select-list-node-children"
        v-if="valueNotEmpty(item[fieldsName.children])"
        v-show="expandList.includes(`${item[fieldsName.key]}`)"
      >
        <custom-tree-select-list
          v-model="value"
          :items="item[fieldsName.children]"
          :checkStrictly="checkStrictly"
          :fieldsName="fieldsName"
          :expandList="expandList"
          @input="(v) => $emit('input', v)"
          @onExpandChecked="(v) => $emit('onExpandChecked', v)"
          @onExpandChange="(_, v) => $emit('onExpandChange', _, v)"
        />
        <!-- @input 作用：v-model值更新 -->
      </view>
    </view>
  </view>
</template>

<script>
export default {
  name: "custom-tree-select-list",
  props: {
    value: {
      type: Array,
      default: () => [],
    },
    items: {
      type: Array,
      default: () => [],
    },
    fieldsName: {
      type: Object,
      default: () => ({
        key: "key",
        label: "label",
        children: "children",
      }),
    },
    checkStrictly: {
      // 是否严格的遵守父子不互相关联
      // 默认值：false
      // 注意：如果设置为 true，则选中父节点时，自动选中子节点；
      type: Boolean,
      default: false,
    },
    expandList: {
      // 展开项
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {};
  },
  methods: {
    valueNotEmpty(value) {
      return value !== undefined && value.length !== 0;
    },
    toObject(values) {
      const obj = {};
      values.forEach((v) => (obj[v] = true));
      return obj;
    },
    hanldeCheckStrictly(values, current) {
      // 生成Map对象
      const map = new Map();
      values.forEach((v) => map.set(v, true));
      const fieldsName = this.fieldsName;

      // 递归处理children
      const handleChildren = (items = [], value, filter) => {
        if (!value || value.length === 0) return;
        value.forEach((child) => {
          const keyValue = `${child[fieldsName.key]}`;
          if (filter && map.has(keyValue)) {
            const index = items.indexOf(keyValue);
            index !== -1 && items.splice(index, 1);
          } else if (!filter && !map.has(child[fieldsName.key])) {
            items.push(keyValue);
          }
          handleChildren(items, child[fieldsName.children], filter);
        });
      };
      const keyValue = `${current[fieldsName.key]}`;
      const filter = map.has(keyValue);
      if (filter) {
        const index = values.indexOf(keyValue);
        index !== -1 && values.splice(index, 1);
      } else {
        values.push(keyValue);
      }

      handleChildren(values, current.children, filter);
      return values;
    },
    onChecked(value, current) {
      if (this.checkStrictly) {
        const data = this.hanldeCheckStrictly(this.value, current);
        return this.$emit("input", data);
      }

      const checkedList = this.value;
      const keyValue = `${value}`;
      if (!checkedList.includes(keyValue)) {
        checkedList.push(keyValue);
      } else {
        const index = checkedList.indexOf(keyValue);
        index !== -1 && checkedList.splice(index, 1);
      }
      // 更新父组件v-model值
      this.$emit("input", checkedList);
    },
    onExpandChecked(value) {
      if (this.expandList.includes(value) || this.expandList.includes(`${value}`)) {
        const index = this.expandList.indexOf(value);
        index !== -1 && this.expandList.splice(index, 1);
      } else {
        this.expandList.push(value);
      }
      this.$emit("onExpandChange", value, this.expandList);
    },
  },
};
</script>

<style lang="scss" scoped>
.custom-tree-select-list .tree-select-list-node {
  width: 100%;

  .tree-select-list-node-holder {
    width: 100%;
    display: flex;
    flex-direction: row;
    align-items: center;
    padding: 4px 0px;

    .tree-select-list-node-switcher {
      opacity: 1;
      width: 26px;
      height: 24px;
      font-size: 10px;
      position: relative;

      transform: rotate(0deg);
      transform-origin: center;
      transition: transform 0.2s;

      &::after {
        content: "";
        top: calc(50% - 6px);
        right: calc(50% - 3px);
        position: absolute;
        border: 6px solid #000;
        border-right-width: 0px;
        border-top-color: transparent;
        border-right-color: transparent;
        border-bottom-color: transparent;
      }

      &_disbaled {
        opacity: 0;
      }

      &_open {
        transform: rotate(90deg);
      }
    }

    .tree-select-list-node-content {
      width: 100%;
    }
  }

  .tree-select-list-node-children {
    padding-left: 16px;
    box-sizing: border-box;
    background-color: #fff;
  }
}
</style>
